/*
 * Copyright (C) 2022-2022. Huawei Technologies Co., Ltd. All rights reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.openlookeng.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.openlookeng.core.boot.dto.LoginUserInfoDTO;
import com.openlookeng.core.boot.jwt.model.Audience;
import com.openlookeng.core.boot.utils.JwtTokenUtil;
import com.openlookeng.core.mp.base.BaseServiceImpl;
import com.openlookeng.dto.ClusterConfigModifyDTO;
import com.openlookeng.dto.OpenlookengClusterConfigDTO;
import com.openlookeng.entity.OpenlookengCluster;
import com.openlookeng.entity.OpenlookengClusterConfig;
import com.openlookeng.enums.ConfigSynStateEnum;
import com.openlookeng.exception.BizException;
import com.openlookeng.mapper.OpenlookengClusterConfigMapper;
import com.openlookeng.service.IOpenLookengService;
import com.openlookeng.service.IOpenlookengClusterConfigService;
import com.openlookeng.service.IOpenlookengClusterService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Slf4j
public class OpenlookengClusterConfigServiceImpl
        extends BaseServiceImpl<OpenlookengClusterConfigMapper, OpenlookengClusterConfig>
        implements IOpenlookengClusterConfigService
{
    @Autowired
    private Audience audience;
    @Autowired
    @Lazy
    private IOpenLookengService openLookengService;
    @Autowired
    @Lazy
    private IOpenlookengClusterService iOpenlookengClusterService;
    @Autowired
    @Lazy
    private IOpenlookengClusterConfigService openlookengClusterConfigService;

    @Override
    public List<OpenlookengClusterConfig> listConfigByClusterCode(String clusterCode)
    {
        LambdaQueryWrapper<OpenlookengClusterConfig> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(OpenlookengClusterConfig::getClusterCode, clusterCode);
        return this.list(queryWrapper);
    }

    @Override
    public Map<String, Object> getClusterConfig(String clusterCode)
    {
        Map<String, Object> res = new HashMap<>();
        List<OpenlookengClusterConfig> clusterConfigList = listConfigByClusterCode(clusterCode);
        //group by type
        Map<String, List<OpenlookengClusterConfig>> typeConfigMap = clusterConfigList.stream().collect(Collectors.groupingBy(OpenlookengClusterConfig::getType));
        typeConfigMap.forEach((k, v) -> {
            //Then according to the configuration components
            Map<String, List<OpenlookengClusterConfig>> groupConfigMap = v.stream().collect(Collectors.groupingBy(OpenlookengClusterConfig::getConfigGroup));
            Map<String, Object> data = new HashMap<>();
            List<Map> tabList = new ArrayList<>();
            groupConfigMap.forEach((kk, vv) -> {
                String key = k + kk;
                Map<String, Object> dataMap = getProValMap(vv);
                data.put(key, dataMap);
                Map tabMap = new HashMap();
                tabMap.put("title", "连接器" + kk);
                tabMap.put("name", kk);
                tabMap.put("configGroup", kk);
                tabList.add(tabMap);
            });
            data.put(k + "Tabs", tabList);
            data.put(k + "TabIndex", groupConfigMap.size());
            data.put(k + "TabsValue", "1");
            res.put(k, data);
        });
        return res;
    }

    @Override
    public List<OpenlookengClusterConfig> listSysDefaultConfig(String version)
    {
        LambdaQueryWrapper<OpenlookengClusterConfig> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(OpenlookengClusterConfig::getCreateUserId, -1); // 系统默认的配置
        queryWrapper.eq(OpenlookengClusterConfig::getVersion, version);
        queryWrapper.orderByAsc(OpenlookengClusterConfig::getOrderNum);
        return this.list(queryWrapper);
    }

    @Override
    public List<OpenlookengClusterConfig> listClusterConfig(Integer createUserId, Integer clusterId, String version)
    {
        LambdaQueryWrapper<OpenlookengClusterConfig> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(OpenlookengClusterConfig::getVersion, version);
        queryWrapper.eq(OpenlookengClusterConfig::getOpenlookengClusterId, clusterId);
        queryWrapper.eq(OpenlookengClusterConfig::getCreateUserId, createUserId);
        queryWrapper.orderByAsc(OpenlookengClusterConfig::getOrderNum);
        return this.list(queryWrapper);
    }

    @Override
    public void delClusterConfig(Integer createUserId, Integer clusterId, String version)
    {
        LambdaQueryWrapper<OpenlookengClusterConfig> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(OpenlookengClusterConfig::getVersion, version);
        queryWrapper.eq(OpenlookengClusterConfig::getOpenlookengClusterId, clusterId);
        queryWrapper.eq(OpenlookengClusterConfig::getCreateUserId, createUserId);
        this.remove(queryWrapper);
    }

    @Override
    public List<String> listConfigGroup(Integer createUserId, Integer clusterId, String version)
    {
        List<OpenlookengClusterConfig> configList = listClusterConfig(createUserId, clusterId, version);
        List<String> res = configList.stream().map(a -> a.getConfigGroup()).distinct().collect(Collectors.toList());
        return res;
    }

    @Override
    public List<Map> listSysDefaultConfigOptions(String version)
    {
        List<Map> res = new ArrayList<>();
        List<OpenlookengClusterConfig> configList = this.listSysDefaultConfig(version);
        Map<String, List<OpenlookengClusterConfig>> configNameMap = configList.stream().collect(Collectors.groupingBy(OpenlookengClusterConfig::getTypeGroupName));
        configNameMap.forEach((k, v) -> {
            Map optionData = new HashMap();
            optionData.put("label", k);
            List<Map> options = new ArrayList<>();
            Map<String, List<OpenlookengClusterConfig>> configTypeMap = v.stream().collect(Collectors.groupingBy(OpenlookengClusterConfig::getType));
            configTypeMap.forEach((kk, vv) -> {
                Map childOption = new HashMap();
                childOption.put("label", kk);
                OpenlookengClusterConfig configTypeName = vv.stream().filter(a -> StringUtils.isNotBlank(a.getTypeName())).findFirst().orElse(null);
                if (configTypeName != null) {
                    childOption.put("value", configTypeName.getTypeName());
                }
                else {
                    childOption.put("value", kk);
                }
                options.add(childOption);
            });
            optionData.put("options", options);
            res.add(optionData);
        });
        return res;
    }

    @Override
    public void deleteClusterConfig()
    {
        LoginUserInfoDTO loginUserInfoDTO = JwtTokenUtil.getCurrentLoginUser(audience);
        String clusterCode = loginUserInfoDTO.getId();
        LambdaQueryWrapper<OpenlookengClusterConfig> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(OpenlookengClusterConfig::getCreateUserId, loginUserInfoDTO.getId());
        queryWrapper.eq(OpenlookengClusterConfig::getClusterCode, clusterCode);
        this.remove(queryWrapper);
    }

    public Map<String, Object> getProValMap(List<OpenlookengClusterConfig> clusterConfigList)
    {
        Map<String, List<OpenlookengClusterConfig>> proValMap = clusterConfigList.stream().collect(Collectors.groupingBy(OpenlookengClusterConfig::getProKeyName));
        Map<String, Object> dataMap = new HashMap<>();
        proValMap.forEach((k, v) -> {
            OpenlookengClusterConfig clusterConfig = v.get(0);
            String proVal = clusterConfig.getProValue();
            dataMap.put(k, proVal);
            if ("true".equalsIgnoreCase(proVal) || "false".equalsIgnoreCase(proVal)) {
                boolean b = Boolean.valueOf(proVal).booleanValue();
                dataMap.put(k, b);
            }
        });
        return dataMap;
    }

    private List<OpenlookengClusterConfig> uodateOrDelConfig(OpenlookengClusterConfigDTO dto)
    {
        Integer clusterId = dto.getOpenlookengClusterId();
        OpenlookengClusterConfig obj = this.getById(dto.getId());
        List<OpenlookengClusterConfig> list = new ArrayList<>();
        if (clusterId.intValue() == -1) {
            String clusterCode = obj.getClusterCode();
            String version = obj.getVersion();
            LambdaQueryWrapper<OpenlookengClusterConfig> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(OpenlookengClusterConfig::getClusterCode, clusterCode);
            queryWrapper.eq(OpenlookengClusterConfig::getProKeyName, dto.getProKeyName());
            queryWrapper.eq(OpenlookengClusterConfig::getVersion, version);
            list = this.list(queryWrapper);
            list.forEach(item -> {
                item.setProValue(dto.getProValue());
                item.setDescStr(dto.getDescStr());
                item.setFilePath(dto.getFilePath());
            });
        }
        else {
            obj.setProValue(dto.getProValue());
            obj.setDescStr(dto.getDescStr());
            obj.setFilePath(dto.getFilePath());
            list.add(obj);
        }
        return list;
    }

    @Override
    public void modifyConfigList(ClusterConfigModifyDTO clusterConfigModifyDTO)
    {
        Integer clusterId = clusterConfigModifyDTO.getClusterId();
        LoginUserInfoDTO userInfoDTO = JwtTokenUtil.getCurrentLoginUser(audience);
        List<OpenlookengClusterConfigDTO> configList = clusterConfigModifyDTO.getConfigList();
        OpenlookengClusterConfigDTO discoveryUrl = configList.stream().filter(a -> a.getProKeyName().equalsIgnoreCase("discovery.uri")).findFirst().get();
        String proVal = discoveryUrl.getProValue();
        if (proVal.contains("127.0.0.1")) {
            throw new BizException("discovery.uri 不能为:" + proVal);
        }
        List<OpenlookengClusterConfig> saveList = configList.stream().map(a -> {
            OpenlookengClusterConfig config = new OpenlookengClusterConfig();
            BeanUtil.copyProperties(a, config);
            config.setId(null);
            config.setClusterCode(userInfoDTO.getId());
            config.setCreateUserId(Integer.valueOf(userInfoDTO.getId()));
            return config;
        }).collect(Collectors.toList());
        List<OpenlookengClusterConfig> allConfigList = new ArrayList<>(); //要保存的所有配置
        List<OpenlookengCluster> clusterList = new ArrayList<>();
        LambdaQueryWrapper<OpenlookengClusterConfig> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(OpenlookengClusterConfig::getCreateUserId, userInfoDTO.getId());
        queryWrapper.eq(OpenlookengClusterConfig::getVersion, clusterConfigModifyDTO.getVersion());
        //如果不是集群的配置改动则只修改单个节点的数据
        if (clusterId != -1) {
            queryWrapper.eq(OpenlookengClusterConfig::getOpenlookengClusterId, clusterId);
            OpenlookengCluster openlookengCluster = iOpenlookengClusterService.getById(clusterId);
            clusterList.add(openlookengCluster);
        }
        else {
            allConfigList.addAll(saveList);
            clusterList = iOpenlookengClusterService.listCurrentUserCluster();
        }
        clusterList.forEach(a -> {
            List nodeSaveList = configList.stream().map(b -> {
                OpenlookengClusterConfig config = new OpenlookengClusterConfig();
                BeanUtil.copyProperties(b, config);
                config.setId(null);
                config.setCreateUserId(Integer.valueOf(userInfoDTO.getId()));
                config.setOpenlookengClusterId(a.getId());
                config.setClusterCode(userInfoDTO.getId());
                return config;
            }).collect(Collectors.toList());
            allConfigList.addAll(nodeSaveList);
            a.setConfigSynState(ConfigSynStateEnum.UN_SYN.getType());
        });
        iOpenlookengClusterService.saveOrUpdateBatch(clusterList);
        this.remove(queryWrapper); //删除
        this.saveBatch(allConfigList);
    }

    @Override
    public List<String> listDiscoveryUri(String clusterCode, String version)
    {
        LambdaQueryWrapper<OpenlookengClusterConfig> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(OpenlookengClusterConfig::getClusterCode, clusterCode);
        queryWrapper.eq(OpenlookengClusterConfig::getCreateUserId, clusterCode);
        queryWrapper.eq(OpenlookengClusterConfig::getVersion, version);
        queryWrapper.eq(OpenlookengClusterConfig::getProKeyName, "discovery.uri");
        List<OpenlookengClusterConfig> configList = this.list(queryWrapper);
        List<String> discoveryUri = configList.stream().map(a -> a.getProValue()).distinct().collect(Collectors.toList());
        return discoveryUri;
    }

    @Override
    public List<String> listDiscoveryUri(Integer nodeId, String version)
    {
        LambdaQueryWrapper<OpenlookengClusterConfig> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(OpenlookengClusterConfig::getOpenlookengClusterId, nodeId);
        queryWrapper.eq(OpenlookengClusterConfig::getVersion, version);
        queryWrapper.eq(OpenlookengClusterConfig::getProKeyName, "discovery.uri");
        List<OpenlookengClusterConfig> configList = this.list(queryWrapper);
        List<String> discoveryUri = configList.stream().map(a -> a.getProValue()).distinct().collect(Collectors.toList());
        return discoveryUri;
    }

    @Override
    public void copyDefaultConfig(String fromVersion, String toVersion)
    {
        if (fromVersion.equals(toVersion)) {
            throw new BizException("两个版本不能一致");
        }
        QueryWrapper<OpenlookengClusterConfig> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("version", toVersion);
        queryWrapper.eq("create_user_id", -1);
        if (this.baseMapper.selectCount(queryWrapper) == 0) {
            QueryWrapper<OpenlookengClusterConfig> wrapper = new QueryWrapper<>();
            wrapper.eq("version", fromVersion);
            wrapper.eq("create_user_id", -1);
            List<OpenlookengClusterConfig> openlookengClusterConfigs = this.baseMapper.selectList(wrapper);
            openlookengClusterConfigs.stream().forEach(i -> {
                i.setId(null);
                i.setVersion(toVersion);
                i.setCreateTime(DateUtil.date().toString());
            });
            openlookengClusterConfigService.saveBatch(openlookengClusterConfigs);
        }
    }
}
